import numpy as np
import matplotlib.pyplot as plt
# Set random seed for reproducibility
42)
np.random.seed(
# Configure plotting style
'ggplot')
plt.style.use('figure.figsize'] = [12, 6]
plt.rcParams['font.size'] = 12 plt.rcParams[
Forward and Futures Contracts
Introduction
Forward and futures contracts are fundamental derivatives in financial markets. While they serve similar purposes, they have important differences in their structure and trading mechanisms.
Forward Contracts
A forward contract is a customized agreement between two parties to buy or sell an asset at a specified future date for a price agreed upon today. Key characteristics:
- Private Agreement:
- Traded over-the-counter (OTC)
- Customized terms
- No intermediary
- Settlement:
- Single payment at maturity
- Physical or cash settlement
- No intermediate cash flows
- Counterparty Risk:
- No guarantee mechanism
- Credit risk is a concern
- Default risk must be considered
Futures Contracts
A futures contract is a standardized agreement traded on exchanges. Key characteristics:
- Exchange-Traded:
- Standardized terms
- Highly liquid
- Price transparency
- Settlement:
- Daily marking-to-market
- Margin requirements
- Clearinghouse guarantee
- Risk Management:
- Minimal counterparty risk
- Regular margin adjustments
- Exchange oversight
Understanding Cost of Carry
The cost of carry is a fundamental concept in forward and futures pricing. It represents the net cost of holding an asset over time. Let’s break this down with a practical example.
Example: Agricultural Commodities
Consider a wheat farmer and a flour mill:
- Immediate Purchase (Spot Market):
- Buy wheat today at spot price
- Pay for storage
- Pay for insurance
- Incur financing costs
- Risk of quality deterioration
- Forward Contract:
- Lock in price today
- Delivery in 6 months
- Seller bears storage costs
- No immediate capital outlay
- Quality guaranteed at delivery
Components of Cost of Carry
- Storage Costs:
- Physical storage space
- Handling and maintenance
- Insurance
- Financing Costs:
- Interest on borrowed funds
- Opportunity cost of capital
- Benefits of Holding:
- Convenience yield
- Income (e.g., dividends)
- Quality optionality
def calculate_forward_price(S0, r, q, T, dividends=None):
"""
Calculate forward price using the cost of carry model.
Parameters:
-----------
S0 : float
Spot price
r : float
Risk-free rate (annualized)
q : float
Convenience yield or dividend yield (annualized)
T : float
Time to maturity in years
dividends : list of tuples, optional
List of (amount, time) tuples for discrete dividends
Returns:
--------
float
Forward price
"""
# Basic forward price without dividends
= S0 * np.exp((r - q) * T)
F
# Adjust for discrete dividends if provided
if dividends:
for D, t in dividends:
-= D * np.exp(r * (T - t))
F
return F
# Example parameters
= 100 # Spot price
S0 = 0.05 # Risk-free rate (5%)
r = 0.02 # Convenience yield (2%)
q = 1.0 # One year to maturity
T
# Calculate forward price without dividends
= calculate_forward_price(S0, r, q, T)
F_no_div
# Calculate with some discrete dividends
= [(2.0, 0.25), (2.0, 0.75)] # $2 dividend at 3 months and 9 months
dividends = calculate_forward_price(S0, r, q, T, dividends)
F_with_div
print(f"Forward Price (No Dividends): ${F_no_div:.2f}")
print(f"Forward Price (With Dividends): ${F_with_div:.2f}")
# Plot forward prices for different maturities
= np.linspace(0, 2, 100)
T_range = [calculate_forward_price(S0, r, q, t) for t in T_range]
F_range_no_div = [calculate_forward_price(S0, r, q, t, dividends) for t in T_range]
F_range_with_div
=(12, 6))
plt.figure(figsize='No Dividends', linewidth=2)
plt.plot(T_range, F_range_no_div, label='With Dividends', linewidth=2)
plt.plot(T_range, F_range_with_div, label=S0, color='k', linestyle='--', label='Spot Price')
plt.axhline(y
'Forward Prices vs Time to Maturity')
plt.title('Time to Maturity (Years)')
plt.xlabel('Forward Price ($)')
plt.ylabel(
plt.legend()True)
plt.grid( plt.show()
Forward Price (No Dividends): $103.05
Forward Price (With Dividends): $98.94
Forward Price Formula and Its Components
The forward price formula is derived from the principle of no-arbitrage and incorporates several key components:
Basic Formula
The forward price \(F\) for an asset with spot price \(S_0\) is given by:
\[F = S_0 e^{(r-q)T} - \sum_{i=1}^N D_i e^{r(T-t_i)}\]
where: - \(F\) is the forward price to be paid at time \(T\) - \(S_0\) is the current spot price - \(r\) is the risk-free interest rate (continuous compounding) - \(q\) is the continuous dividend yield or convenience yield - \(T\) is the time to maturity in years - \(D_i\) is a discrete dividend paid at time \(t_i\) where \(0 < t_i < T\)
Key Components Explained
- Spot Price (\(S_0\)):
- Current market price of the asset
- Starting point for forward pricing
- Risk-Free Rate (\(r\)):
- Opportunity cost of funds
- Usually based on government securities
- Assumed to be constant for simplicity
- Dividend/Convenience Yield (\(q\)):
- Continuous yield from holding the asset
- For stocks: dividend yield
- For commodities: convenience yield
- Time Factor (\(T\)):
- Time to maturity in years
- Affects the compounding of carrying costs
- Discrete Dividends (\(D_i\)):
- Known dividend payments
- Adjusted for time value of money
- Reduces the forward price
def analyze_forward_price_sensitivity(S0=100, r=0.05, q=0.02, T=1.0, dividends=None):
"""
Analyze how forward prices change with different parameters.
Parameters are same as calculate_forward_price function.
"""
# Parameter ranges
= np.linspace(0, 0.1, 50) # 0% to 10%
r_range = np.linspace(0, 0.1, 50) # 0% to 10%
q_range = np.linspace(50, 150, 50) # $50 to $150
S0_range
# Calculate sensitivity to interest rates
= [calculate_forward_price(S0, r_i, q, T, dividends) for r_i in r_range]
F_r
# Calculate sensitivity to convenience yield
= [calculate_forward_price(S0, r, q_i, T, dividends) for q_i in q_range]
F_q
# Calculate sensitivity to spot price
= [calculate_forward_price(S0_i, r, q, T, dividends) for S0_i in S0_range]
F_S0
# Create subplots
= plt.subplots(1, 3, figsize=(18, 5))
fig, (ax1, ax2, ax3)
# Plot sensitivity to interest rate
* 100, F_r, 'b-', linewidth=2)
ax1.plot(r_range 'Forward Price vs Interest Rate')
ax1.set_title('Interest Rate (%)')
ax1.set_xlabel('Forward Price ($)')
ax1.set_ylabel(True)
ax1.grid(
# Plot sensitivity to convenience yield
* 100, F_q, 'g-', linewidth=2)
ax2.plot(q_range 'Forward Price vs Convenience Yield')
ax2.set_title('Convenience Yield (%)')
ax2.set_xlabel('Forward Price ($)')
ax2.set_ylabel(True)
ax2.grid(
# Plot sensitivity to spot price
'r-', linewidth=2)
ax3.plot(S0_range, F_S0, 'k--', label='Spot Price')
ax3.plot(S0_range, S0_range, 'Forward Price vs Spot Price')
ax3.set_title('Spot Price ($)')
ax3.set_xlabel('Forward Price ($)')
ax3.set_ylabel(
ax3.legend()True)
ax3.grid(
plt.tight_layout()
plt.show()
# Run sensitivity analysis
print("Analyzing Forward Price Sensitivity to Various Parameters...")
analyze_forward_price_sensitivity()
Analyzing Forward Price Sensitivity to Various Parameters...
def simulate_futures_margin(S0=100, mu=0.1, sigma=0.2, T=1.0, n_steps=252, n_paths=5):
"""
Simulate futures margin calls using geometric Brownian motion for the underlying.
Parameters:
-----------
S0 : float
Initial spot price
mu : float
Drift rate (annualized)
sigma : float
Volatility (annualized)
T : float
Time to maturity in years
n_steps : int
Number of time steps (trading days)
n_paths : int
Number of paths to simulate
"""
# Time grid
= T/n_steps
dt = np.linspace(0, T, n_steps)
t
# Initialize arrays
= np.zeros((n_paths, n_steps))
S 0] = S0
S[:,
# Simulate price paths
for i in range(n_paths):
for j in range(1, n_steps):
= np.sqrt(dt) * np.random.normal()
dW = S[i, j-1] * np.exp((mu - 0.5*sigma**2)*dt + sigma*dW)
S[i, j]
# Calculate daily margin calls
= np.diff(S, axis=1)
margin_calls
# Plot results
=(12, 8))
plt.figure(figsize
# Plot price paths
2, 1, 1)
plt.subplot(for i in range(n_paths):
=f'Path {i+1}')
plt.plot(t, S[i], label'Simulated Price Paths')
plt.title('Time (years)')
plt.xlabel('Price ($)')
plt.ylabel(
plt.legend()True)
plt.grid(
# Plot margin calls
2, 1, 2)
plt.subplot(for i in range(n_paths):
1:], margin_calls[i], label=f'Path {i+1}')
plt.plot(t['Daily Margin Calls')
plt.title('Time (years)')
plt.xlabel('Margin Call ($)')
plt.ylabel(=0, color='k', linestyle='--')
plt.axhline(y
plt.legend()True)
plt.grid(
plt.tight_layout()
plt.show()
# Print summary statistics
print("\nMargin Call Statistics:")
print(f"Average Daily Margin Call: ${np.mean(margin_calls):.2f}")
print(f"Max Positive Margin Call: ${np.max(margin_calls):.2f}")
print(f"Max Negative Margin Call: ${np.min(margin_calls):.2f}")
print(f"Standard Deviation: ${np.std(margin_calls):.2f}")
# Run simulation
print("Simulating Futures Margin Calls...")
simulate_futures_margin()
Simulating Futures Margin Calls...
Margin Call Statistics:
Average Daily Margin Call: $0.11
Max Positive Margin Call: $4.97
Max Negative Margin Call: $-5.27
Standard Deviation: $1.48